﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;

namespace DotNETX.Authentication.Db
{
    public abstract class DbTokenManager : ITokenManager
    {
        public const int CODE_STATUS_ACTIVE = 1;
        public const int CODE_STATUS_DEACTIVE = 0;

        private int lastVistTimeToleranceSecond = 5;
        public int LastVistTimeToleranceSecond
        {
            get { return lastVistTimeToleranceSecond; }
            set
            {
                lastVistTimeToleranceSecond = value;
            }
        }

        #region ITokenManager

        public virtual string New()
        {
            return Guid.NewGuid().ToString();
        }

        public virtual int Register(string token, object uniqueId)
        {
            if (token == null || uniqueId == null) return -1;
            try
            {
                if (!TryOpenConnection(Connection)) return int.MinValue;
                IDbCommand cmd = Connection.CreateCommand();
                cmd.Transaction = Connection.BeginTransaction();
                try
                {
                    string sql = null;
                    string tokenExp = null;
                    string uniqueIdExp = null;
                    string loginTimeExp = null;
                    string lastVisitTimeExp = null;
                    DateTime now = DateTime.Now;
                    tokenExp = CreateParam(cmd, "p_token", token);
                    uniqueIdExp = CreateParam(cmd, "p_uniqueId", uniqueId);
                    loginTimeExp = CreateParam(cmd, "p_loginTime", now);
                    lastVisitTimeExp = CreateParam(cmd, "p_lastVisitTime", now);

                    if (string.IsNullOrEmpty(StatusColumn))
                    {
                        sql = string.Format("insert into {0}({1},{2},{3},{4}) values({5},{6},{7},{8})", TableName, TokenColumn, UniqueIdColumn, LoginTimeColumn, LastVisitTimeColumn, tokenExp, uniqueIdExp, loginTimeExp, lastVisitTimeExp);
                    }
                    else
                    {
                        string statusExp = null;
                        statusExp = CreateParam(cmd, "p_status", CODE_STATUS_ACTIVE);
                        sql = string.Format("insert into {0}({1},{2},{3},{4},{5}) values({6},{7},{8},{9},{10})", TableName, TokenColumn, UniqueIdColumn, LoginTimeColumn, LastVisitTimeColumn, StatusColumn, tokenExp, uniqueIdExp, loginTimeExp, lastVisitTimeExp, statusExp);
                    }
                    cmd.CommandText = sql;

                    int i = cmd.ExecuteNonQuery();
                    if (i == 1)
                    {
                        cmd.Transaction.Commit();
                        return 0;
                    }
                    else
                    {
                        cmd.Transaction.Rollback();
                        return -2;
                    }
                }
                catch (System.Exception ex1)
                {
                    cmd.Transaction.Rollback();
                    throw ex1;
                }
            }
            catch (Exception ex)
            {
                return int.MinValue;
            }
            finally
            {
                Connection.Close();
            }
        }

        public virtual int Unregister(string token)
        {
            if (token == null) return -1;
            try
            {
                if (!TryOpenConnection(Connection)) return int.MinValue;
                IDbCommand cmd = Connection.CreateCommand();
                cmd.Transaction = Connection.BeginTransaction();
                try
                {
                    string sql = null;
                    string tokenExp = null;

                    if (string.IsNullOrEmpty(StatusColumn))
                    {
                        tokenExp = CreateParam(cmd, "p_token", token);
                        sql = string.Format("delete from {0} where {1}={2}", TableName, TokenColumn, tokenExp);
                    }
                    else
                    {
                        string statusExp = null;
                        statusExp = CreateParam(cmd, "p_status", CODE_STATUS_DEACTIVE);
                        tokenExp = CreateParam(cmd, "p_token", token);
                        sql = string.Format("update {0} set {2}={3} where {1}={4}", TableName, TokenColumn, StatusColumn, statusExp, tokenExp);
                    }

                    cmd.CommandText = sql;

                    int i = cmd.ExecuteNonQuery();
                    if (i == 1)
                    {
                        cmd.Transaction.Commit();
                        return 0;
                    }
                    else
                    {
                        cmd.Transaction.Rollback();
                        return -2;
                    }
                }
                catch (System.Exception ex1)
                {
                    cmd.Transaction.Rollback();
                    throw ex1;
                }
            }
            catch (Exception ex)
            {
                return int.MinValue;
            }
            finally
            {
                Connection.Close();
            }
        }

        private DateTime TryConvert2Date(object val)
        {
            if (val is DateTime)
            {
                return (DateTime)val;
            }
            else
            {
                DateTime dt = new DateTime();
                if (null == val) return dt;
                else
                {
                    DateTime.TryParse(val.ToString(), out dt);
                    return dt;
                }
            }
        }

        public virtual UserSession GetSession(string token)
        {
            if (token == null) return null;
            UserSession session = null;
            try
            {
                if (!TryOpenConnection(Connection)) return null;
                IDbCommand cmd = Connection.CreateCommand();

                string sql = null;
                string tokenExp = null;
                tokenExp = CreateParam(cmd, "p_token", token);

                if (string.IsNullOrEmpty(StatusColumn))
                {
                    sql = string.Format("select {1} as c_Token, {2} as c_UniqueId, {3} as c_LoginTime, {4} as c_LastVistTime from {0} where {1}={5}", TableName, TokenColumn, UniqueIdColumn, LoginTimeColumn, LastVisitTimeColumn, tokenExp);
                }
                else
                {
                    string statusExp = null;
                    statusExp = CreateParam(cmd, "p_status", CODE_STATUS_ACTIVE);
                    sql = string.Format("select {1} as c_Token, {2} as c_UniqueId, {3} as c_LoginTime, {4} as c_LastVistTime from {0} where {1}={6} and {5}={7}", TableName, TokenColumn, UniqueIdColumn, LoginTimeColumn, LastVisitTimeColumn, StatusColumn, tokenExp, statusExp);
                }

                cmd.CommandText = sql;

                using (IDataReader reader = cmd.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        session = new UserSession() { Token = reader.GetValue(0).ToString(), UniqueId = reader.GetValue(1), LoginTime = TryConvert2Date(reader.GetValue(2)), LastVistTime = TryConvert2Date(reader.GetValue(3)) };
                    }
                    else
                    {
                        return null;
                    }
                }
            }
            catch (Exception ex)
            {
                return null;
            }
            if ((DateTime.Now - session.LastVistTime).TotalSeconds > LastVistTimeToleranceSecond)
            try
            {
                IDbCommand cmd = Connection.CreateCommand();
                cmd.Transaction = Connection.BeginTransaction();
                try
                {
                    string lastVisitTimeExp = null;
                    lastVisitTimeExp = CreateParam(cmd, "p_lastVisitTim", DateTime.Now);
                    string tokenExp = null;
                    tokenExp = CreateParam(cmd, "p_token", token);
                    cmd.CommandText = string.Format("update {0} set {2}={4} where {1}={3}", TableName, TokenColumn, LastVisitTimeColumn, tokenExp, lastVisitTimeExp);

                    int i = cmd.ExecuteNonQuery();
                    if (i <= 1)
                    {
                        cmd.Transaction.Commit();
                    }
                    else
                    {
                        cmd.Transaction.Rollback();
                    }
                }
                catch (System.Exception ex1)
                {
                    cmd.Transaction.Rollback();
                    throw ex1;
                }
            }
            catch (Exception ex)
            {
                //throw new Exception("Token查找发生异常！");
            }
            finally
            {
                Connection.Close();
            }
            return session;
        }

        #endregion

        public abstract string CreateParam(IDbCommand cmd, string paramName, object paramValue);
        public virtual bool TryOpenConnection(IDbConnection connection)
        {
            if (connection == null) return false;
            try
            {
                if (connection.State == ConnectionState.Closed)
                {
                    connection.Open();
                }
                else if (connection.State == ConnectionState.Broken)
                {
                    connection.Close();
                    connection.Open();
                }
                return true;
            }
            catch (System.Exception ex)
            {
                return false;
            }
        }

        public IDbConnection Connection { get; set; }

        public string TableName { get; set; }
        public string TokenColumn { get; set; }
        public string UniqueIdColumn { get; set; }
        public string LoginTimeColumn { get; set; }
        public string LastVisitTimeColumn { get; set; }
        public string StatusColumn { get; set; }

    }
}
